package com.wwj.study.spring.formework.context;

import com.wwj.study.spring.formework.annotion.WAutowired;
import com.wwj.study.spring.formework.annotion.WController;
import com.wwj.study.spring.formework.annotion.WService;
import com.wwj.study.spring.formework.aop.WAopConfig;
import com.wwj.study.spring.formework.beans.WBeanDefinition;
import com.wwj.study.spring.formework.beans.WBeanPostProcessor;
import com.wwj.study.spring.formework.beans.WBeanWrapper;
import com.wwj.study.spring.formework.context.support.WBeanDefinitionReader;
import com.wwj.study.spring.formework.core.WBeanFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * @Author: wuweijun
 * @Date: 2018/12/1 20:48
 */
public class WApplicationContext implements WBeanFactory {
    //配置文件数组
    private String[] configLocations;

    //读取配置文件，配置文件-->beanDefinition
    private WBeanDefinitionReader beanDefinitionReader;

    //最开始的IOC容器
    private Map<String, WBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, WBeanDefinition>();

    //缓存对象
    private Map<String, Object> beanCacheMap = new ConcurrentHashMap<String, Object>();

    //包装后的IOC容器--装饰者模式
    private Map<String, WBeanWrapper> beanWarpperMap = new ConcurrentHashMap<String, WBeanWrapper>();

    //初始化容器
    public WApplicationContext(String... configLocations) {
        this.configLocations = configLocations;
        refresh();
    }

    private void refresh() {
        //定位
        beanDefinitionReader = new WBeanDefinitionReader(configLocations);
        //加载
        List<String> beanDefinitions = beanDefinitionReader.loadBeanDefinitions();
        //注册
        doRegister(beanDefinitions);
        //依赖注入
        doAutowrited();
    }

    //注册
    private void doRegister(List<String> beanDefinitions) {
        //1,默认类
        //2,自定义名称
        //3,接口注入
        for (String beanStr : beanDefinitions) {
            try {
                Class<?> clazz = Class.forName(beanStr);

                if (clazz.isInterface()) {
                    continue;
                }

                //注册默认名称  bean-->beanDefinition
                WBeanDefinition beanDefinition = beanDefinitionReader.registerBean(beanStr);

                if (beanDefinition != null) {
                    //自定义名称
                    String factoryBeanName = "";
                    if (clazz.isAnnotationPresent(WService.class)) {
                        WService wService = clazz.getAnnotation(WService.class);
                        factoryBeanName = wService.value();
                    }
                    if (factoryBeanName.equals("")) {
                        factoryBeanName = beanDefinition.getFactoryBeanName();
                    }
                    beanDefinitionMap.put(factoryBeanName, beanDefinition);
                }

                //注册所有接口
                Class<?>[] classes = clazz.getInterfaces();
                for (Class<?> i : classes) {
                    //接口注入
                    //如果一个接口多个实现类，没办法处理
                    //spring 会报错
                    beanDefinitionMap.put(i.getName(), beanDefinition);
                }

            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }

    //依赖注入,实例化对象注入属性
    private void doAutowrited() {

        //实例化对象
        for (Map.Entry<String, WBeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            //是否懒加载false非懒加载 tu  lazyInit =false  初始化的时候就会自动实例化对象
            if (!beanDefinitionEntry.getValue().isLazyInit()) {
                Object obj = getBean(beanName);
            }
        }

        //开始注入属性
        for (Map.Entry<String, WBeanWrapper> beanWrapperEntry : beanWarpperMap.entrySet()) {
            populateBean(beanWrapperEntry.getKey(), beanWrapperEntry.getValue().getOriginalInstance());
        }
    }

    /*
     * beanName : 类名称
     * instance ：当前对象
     * */
    private void populateBean(String beanName, Object instance) {
        try {
            Class<?> clazz = instance.getClass();

            if (!(clazz.isAnnotationPresent(WController.class) || clazz.isAnnotationPresent(WService.class))) {
                return;
            }

            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                if (!field.isAnnotationPresent(WAutowired.class)) {
                    continue;
                }

                WAutowired autowired = field.getAnnotation(WAutowired.class);
                String autowiredBeanName = autowired.value();
                //注解没有名字
                if (autowiredBeanName.equals("")) {
                    //使用默认字段类型名称
                    autowiredBeanName = field.getType().getName();
                }
                field.setAccessible(true);

                try {
                    //给属性赋值
                    //instance当前对象
                    //field当前字段
                    //autowiredBeanName当前字段应该赋值对象的名称
                    field.set(instance, this.beanWarpperMap.get(autowiredBeanName).getWrapperInstance());
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //通过bean名称，反射实例化对象
    //spring这里使用了增强处理，返回一个BeanWrapper
    @Override
    public Object getBean(String beanName) {
        //获取需要实例化对象的Bean
        WBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        //初始化Bean的 兼听事件对象
        WBeanPostProcessor beanPostProcessor = new WBeanPostProcessor();
        //获取实例化对象
        Object instance = instantionBean(beanDefinition);
        if (instance == null) {
            return null;
        }

        //实例化Bean前置事件
        beanPostProcessor.postProcessBeforeInitialization(instance, beanName);

        //实例化增加Bean
        WBeanWrapper beanWrapper = new WBeanWrapper(instance);
        beanWrapper.setAopConfig(initAopConfig(beanDefinition));
        beanWrapper.setPostProcessor(beanPostProcessor);
        beanWarpperMap.put(beanName, beanWrapper);

        //实例化Bean后置事件
        beanPostProcessor.postProcessAfterInitialization(instance, beanName);

        //返回增强后的实例化对象
        return beanWarpperMap.get(beanName).getWrapperInstance();
    }

    //初始化aop相关配置
    private WAopConfig initAopConfig(WBeanDefinition beanDefinition) {
        //主要的参数
        //method 执行的方法
        //aspect 切面对象
        //methods 前置后置方法数组
        WAopConfig aopConfig=new WAopConfig();

        try {
            //1、获取aop配置文件
            String expression = beanDefinitionReader.getProperties().getProperty("pointCut");
            String[] before = beanDefinitionReader.getProperties().getProperty("aspectBefore").split("\\s");
            String[] after = beanDefinitionReader.getProperties().getProperty("aspectAfter").split("\\s");

            //2、获取类方法集合
            Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
            Method[] methods = clazz.getMethods();

            Pattern pattern = Pattern.compile(expression);

            Class<?> aspectClass = Class.forName(before[0]);

            //3、根据切点找到所有需要切入的方法
            for (Method method : methods) {
                String methodStr = method.toString();
                if (!pattern.matcher(methodStr).matches()) {
                    continue;
                }
                //4、存入容器中Map<method,Aspect>
                Object aspectInstance = aspectClass.newInstance();
                Method[] aspectMethods = new Method[]{aspectClass.getMethod(before[1]), aspectClass.getMethod(after[1])};
                ;
                aopConfig.setAspectMap(method, aspectInstance, aspectMethods);
            }

        }catch (Exception e){
            e.printStackTrace();
        }

        return aopConfig;
    }

    //实例化对象
    private Object instantionBean(WBeanDefinition beanDefinition) {
        Object instance = null;
        String className = beanDefinition.getBeanClassName();
        try {
            //缓存中，是否存在
            if (beanCacheMap.containsKey(className)) {
                instance = beanCacheMap.get(className);
            } else {
                //不存在，实例化一个对象，放入缓存集合中
                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();
                beanCacheMap.put(className, instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }


    public String[] getBeanDefinitions() {
        return this.beanWarpperMap.keySet().toArray(new String[this.beanWarpperMap.size()]);
    }

    public Properties getConfig() {
        return beanDefinitionReader.getProperties();
    }
}
